#include "types.h"

__device__ Mode calcMode(Mode m1, Mode m2)
{
	if (m1 == Constant && m2 == Constant)
		return Constant;

	return Private;
}

__device__ CuString modeText(Mode mode)
{
	if (mode == Constant)
		return "constant";
	else if (mode == Public)
		return "public";
	else
		return "private";
}

//type
__device__ CuString typeName(int id)
{
	if (id == boolean)
		return "boolean";
	else if (id == i8)
		return "i8";
	else if (id == i16)
		return "i16";
	else if (id == i32)
		return "i32";
	else if (id == i64)
		return "i64";
	else if (id == i128)
		return "i128";
	else if (id == u8)
		return "u8";
	else if (id == u16)
		return "u16";
	else if (id == u32)
		return "u32";
	else if (id == u64)
		return "u64";
	else if (id == u128)
		return "u128";
	else if (id == field)
		return "field";

	assert(0);
	return "";
}

__device__ int typeBitSize(int v_type)
{
	if (v_type == Type::boolean)
	{
		return 1;
	}
	if (v_type == Type::i8)
	{
		return 8;
	}
	else if (v_type == Type::i16)
	{
		return 16;
	}
	else if (v_type == Type::i32)
	{
		return 32;
	}
	else if (v_type == Type::i64)
	{
		return 64;
	}
	else if (v_type == Type::i128)
	{
		return 128;
	}
	else if (v_type == Type::u8)
	{
		return 8;
	}
	else if (v_type == Type::u16)
	{
		return 16;
	}
	else if (v_type == Type::u32)
	{
		return 32;
	}
	else if (v_type == Type::u64)
	{
		return 64;
	}
	else if (v_type == Type::u128)
	{
		return 128;
	}
	else if (v_type == Type::field)
	{
		return 253;
	}

	return 0;
}

__device__ bool typeIsSign(int v_type)
{
	if (v_type == Type::i8
		|| v_type == Type::i16
		|| v_type == Type::i32
		|| v_type == Type::i64
		|| v_type == Type::i128)
	{
		return true;
	}
	else if (v_type == Type::u8
		|| v_type == Type::u16
		|| v_type == Type::u32
		|| v_type == Type::u64
		|| v_type == Type::u128)
	{
		return false;
	}

	assert(0);
	return false;
}

__device__ bool typeIsInteger(int v_type)
{
	if (v_type == Type::i8
		|| v_type == Type::i16
		|| v_type == Type::i32
		|| v_type == Type::i64
		|| v_type == Type::i128
		|| v_type == Type::u8
		|| v_type == Type::u16
		|| v_type == Type::u32
		|| v_type == Type::u64
		|| v_type == Type::u128)
	{
		return true;
	}

	return false;
}

__device__ int toRustType(int t)
{
	if (t == Type::field)
		return 2;
	else if (t == Type::i8) 
		return 4;
	else if (t == Type::i16) 
		return 5;
	else if (t == Type::i32) 
		return 6;
	else if (t == Type::i64)
		return 7;
	else if (t == Type::i128) 
		return 8;
	else if (t == Type::u8) 
		return 9;
	else if (t == Type::u16)
		return 10;
	else if (t == Type::u32)
		return 11;
	else if (t == Type::u64)
		return 12;
	else if (t == Type::u128)
		return 13;

	return 0;
}

//Boolean
__device__ Boolean Boolean::constant(bool flag)
{
	Boolean b;
	b.mode = Mode::Constant;
	b.value = flag;
	return b;
}

__device__ Boolean Boolean::create(Mode mode, bool flag)
{
	Boolean b;
	b.mode = mode;
	b.value = flag;
	return b;
}

__device__ Boolean::Boolean()
{
	mode = Constant;
	value = false;
}

__device__ bool Boolean::is_constant() const
{
	return mode == Constant;
}

__device__ CuString Boolean::toString() const
{
	CuString str = value ? "true" : "false";
	str = str + "." + modeText(mode);
	return str;
}

__device__ Boolean Boolean::operator!() const
{
	Boolean ret = *this;
	ret.value = !this->value;
	return ret;
}

__device__ CuDebug operator<<(CuDebug dbg, const Boolean& b)
{
	dbg << b.toString();
	return dbg;
}

__device__ Mode bitArrayMode(const BitArray& list)
{
	bool has_public = false;
	for (int i = 0; i < list.size(); i++)
	{
		if (list[i].mode == Private)
			return Private;
		else if (list[i].mode == Public)
			has_public = true;
	}

	return has_public? Public: Constant;
}

__device__ CuByteArray bits_to_bytes_le(const BitArray& buffer)
{
	uint8_t num = 0;
	CuByteArray ret;
	for (int i = 0; i < buffer.size(); i++)
	{
		num = num | (buffer[i].value << i % 8);
		if ((i + 1) % 8 == 0 || i == buffer.size() - 1)
		{
			ret.push_back((char)num);
			num = 0;
		}
	}
	return ret;
}

__device__ BitArray bytes_to_bits_le(const CuByteArray& buffer)
{
	BitArray ret;
	for (int i = 0; i < buffer.size(); i++)
	{
		uint8_t num = buffer[i];
		for (int bit_idx = 0; bit_idx < 8; bit_idx++)
		{
			Boolean b;
			b.value = (num & (1 << bit_idx)) != 0;
			ret.push_back(b);
		}
	}
	return ret;
}